#include <string>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <memory>
#include "Pocelain.h"
#include "../../controls/OrbitControl.hpp"

using namespace std;
POCELAIN_NAMESPACE_USING

template<class T>
void printMatrix(T mat)
{
    for(size_t r = 0 ; r < mat.numOfRows(); ++r){
        cout<<'|';
        for(size_t c = 0 ; c < mat.numOfCols(); ++c){
            cout << mat[r * mat.numOfCols() + c];
            if(c!=mat.numOfCols()-1){
                cout<<",\t";
            }
        }
        cout<<'|'<<endl;
    }
    cout<<endl;
}
void printMatrix4(const Matrix4 &mat)
{
    printMatrix(mat);
}

class Application: public ApplicationCoreProfile
{
private:
    OpenGLShaderProgram *program= nullptr;
    Texture texture;
    PerspectiveCamera *camera = nullptr;
    Scene *scene = nullptr;
    OpenGLRenderer *renderer = nullptr;
    OrbitControl *control;
public:

    Application(int argc , char **argv)
        :ApplicationCoreProfile(argc,argv)
    {
    }
    ~Application()
    {
        camera->release();
        if(program!=nullptr)
        {
            program->release();
        }
        if(scene!=nullptr)
        {
            scene->release();
        }
        if(renderer!=nullptr)
        {
            renderer->release();
        }
    }

    void init()override
    {
        AutoReleasePool();

        ApplicationCoreProfile::init();

        camera = CreateRef<PerspectiveCamera>(25,mWindowSize.x/mWindowSize.y );

        camera->setPosition(Vector3(10,0,0));

        camera->lookAt(Vector3(0,0,0));

        scene = CreateRef<Scene>();

        auto geo = CreateRef<SphereGeometry>();

        auto material = new MeshNormalMaterial();

        auto mesh = CreateRef<Mesh>(geo,material);

        auto dirLight = CreateRef<DirectionalLight>();

        dirLight->setPosition(Vector3(- 1, 0, 1 ).normalize());

        scene->add( dirLight );

        control = new OrbitControl(mInput,camera);

        scene->add(mesh);

        scene->add(control);

        renderer = new OpenGLRenderer(mWindow,mInput);

        material->setDepthTest(true);

        material->setDepthWrite(true);

    }
    virtual void onResize(int width , int height)override
    {
        ApplicationBase::onResize(width,height);
        camera->setAspect(Real(width)/Real(height));
        camera->updateProjectionMatrix();
    }
    void render(double elapse) override
    {

        control->update(elapse);

        if(mInput->getKey(EKeyCode::Tab))
        {
            auto currState = mInput->getCursorState();
            mInput->setCursorState(
                currState==IInput::ECursorState::Normal?
                    IInput::ECursorState::Disable:
                    IInput::ECursorState::Normal
                );
        }

        renderer->render(scene,camera);
    }
};

int main(int argc,char **argv)
{
    Application app(argc,argv);
    return app.run();
}
